package com.tiny.tinyweb.core.aop;

import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.tiny.tinyweb.core.dao.entity.MapEntity;
import com.tiny.tinyweb.core.log.LogInfo;
import com.tiny.tinyweb.core.utils.FacesUtils;
import com.tiny.tinyweb.core.utils.StringUtils;

/**
 * 实现自{@link org.aopalliance.intercept.MethodInterceptor}接口的用来输出方法执行日志的拦截器类。
 * <ul>
 * <li>信息日志：输出被调用方法执行前后输出开始和结束标志，以及执行时间。</li>
 * <li>调试日志：输出被调用方法接受到的实参和返回值信息。</li>
 * <li>错误日志：输出被调用方法执行异常时的异常信息。</li>
 * </ul>
 *
 * @see {@link org.aopalliance.intercept.MethodInterceptor}
 * @author wxiaobin
 */
public class MethodLogInterceptor implements MethodInterceptor {
    /**
     * 在目标方法执行前后进行环绕拦截，先输出开始日志，然后调用目标方法，最后输出结束日志和执行时间。
     *
     * @param invocation 由{@link org.aopalliance.intercept.MethodInvocation}包装的方法调用对象
     * @return 目标方法的返回值
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 跳过“toString”、“toJSON”和“hashCode”方法
        String method = invocation.getMethod().getName();
        if ("toString".equals(method) || "toJSON".equals(method) || "hashCode".equals(method)) {
            return invocation.proceed();
        }

        // 使用目标方法的具体类实例化一个日志输出对象
        String className = invocation.getThis().getClass().getName();
        if (className.indexOf("$$") > 0) {
            className = className.substring(0, className.indexOf("$$"));
        }
        Logger logger = LoggerFactory.getLogger(ClassUtils.getClass(className));
        // 创建一个保存日志信息的对象
        LogInfo logInfo = new LogInfo();
        // 如果当前环境是JSF上下文，从请求中获得用户的登录信息
        if (FacesContext.getCurrentInstance() != null) {
            HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance()
                    .getExternalContext().getRequest();
            // 获得既有Session，不执行创建处理
            HttpSession session = request == null ? null : request.getSession(false);
            // 获得SessionID
            if (session != null) {
                logInfo.setSessionID(session.getId());
            }
            // 获得登录用户IP地址
            logInfo.setOperatorIP(FacesUtils.getIpAddr(request));
            // 获得登录用户名
            MapEntity userInfo = (MapEntity) FacesUtils.getSessionUser();
            if (userInfo != null && userInfo.get("userNo") != null) {
                logInfo.setOperatorID((String) userInfo.get("userNo"));
            }
        }
        // 获得目标方法名称
        logInfo.setOperation(method);
        // 输出方法执行开始标志
        logInfo.setMessage("begin");
        logger.info(logInfo.toString());
        // 如果目标方法定义了参数，将实参输出到调试日志
        Object[] args = invocation.getArguments();
        if (args.length > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append("arguments: [");
            for (int i = 0; i < args.length - 1; i++) {
                sb.append(StringUtils.toString(args[i]));
                sb.append(", ");
            }
            sb.append(StringUtils.toString(args[args.length - 1]));
            sb.append("]");
            logInfo.setMessage(sb.toString());
            logger.debug(logInfo.toString());
        }

        // 记录异常信息的对象
        boolean hasException = false;
        String exceptionMessage = "";
        // 计时开始
        long beginTime = System.currentTimeMillis();
        // 调用目标方法，并获得其返回值
        Object obj = null;
        try {
            obj = invocation.proceed();
        } catch (Exception e) {
            hasException = true;
            // 有异常发生时，获得异常消息
            if (e.getMessage() == null || "".equals(e.getMessage())) {
                exceptionMessage = ClassUtils.getShortClassName(e.getClass().getName());
            } else {
                exceptionMessage = e.getMessage();
            }
            throw e;
        } finally {
            if (hasException) {
                // 有异常发生时，将异常消息输出到错误日志
                logInfo.setMessage("end (error: %1$s)", exceptionMessage);
                logger.error(logInfo.toString());
            } else {
                // 如果目标方法定义了返回值，获得其返回值并输出到调试日志
                if (!invocation.getMethod().getReturnType().equals(Void.TYPE)) {
                    logInfo.setMessage("results: %1$s", (obj == null ? "null" : StringUtils.toString(obj)));
                    logger.debug(logInfo.toString());
                }
                // 计时结束
                long endTime = System.currentTimeMillis();
                // 输出方法执行结束标志和执行时间
                logInfo.setMessage("end (in: %1$sMS)", (endTime - beginTime));
                logger.info(logInfo.toString());
            }
        }

        // 返回目标方法的返回值
        return obj;
    }
}
